using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;

namespace CodePlex.Resourceful.Amazon.EC2
{

    public enum InstanceType
    {
        Small = 0,
        Large = 1,
        XLarge = 2
    }


    public enum IPProtocol
    {
        TCP = 0,
        UDP = 1,
        ICMP = 2
    }

    public class EC2Client : Client
    {
        private readonly string _accessKeyID;
        private readonly string _secretAccessKey;

        public EC2Client(string accessKeyID, string secretAccessKey)
            : base(new Uri("http://ec2.amazonaws.com/"))
        {
            _accessKeyID = accessKeyID;
            _secretAccessKey = secretAccessKey;

        }


        public ClientResponse RunInstances(string imageID, int minCount, int maxCount, string keyName, string[] securityGroups, string userData, string addressingType, InstanceType instanceType)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["ImageId"] = imageID;
            parameters["MinCount"] = minCount.ToString();
            parameters["MaxCount"] = maxCount.ToString();

            AddOptional(parameters, "KeyName", keyName);
            AddList(parameters, "SecurityGroup", securityGroups);
            AddOptional(parameters, "UserData", userData);
            AddOptional(parameters, "AddressingType", addressingType);
           
            parameters["InstanceType"] = "m1." + instanceType.ToString().ToLower();

            

            return this.Do("RunInstances", parameters);
        }


        public ClientResponse TerminateInstances(params string[] instanceIDs)
        {
            NameValuePairs parameters = new NameValuePairs();
            AddList(parameters, "InstanceId", instanceIDs);
            return this.Do("TerminateInstances", parameters);

        }


        public ClientResponse ResetImageAttribute(string imageID, string attribute)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["ImageId"] = imageID;
            parameters["Attribute"] = attribute;
            return this.Do("ResetImageAttribute", parameters);
        }


        public ClientResponse RegisterImage(string imageLocation)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["ImageLocation"] = imageLocation;
            return this.Do("RegisterImage", parameters);
        }


        public ClientResponse RebootInstances(params string[] instanceIDs)
        {
            NameValuePairs parameters = new NameValuePairs();
            AddList(parameters, "InstanceId", instanceIDs);
            return this.Do("RebootInstances", parameters);

        }


        public ClientResponse ModifyImageAttribute(string imageID, string attribute, string operationType, string[] userIDs, string[] userGroups, string[] productCodes)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["ImageId"] = imageID;
            parameters["Attribute"] = attribute;
            parameters["OperationType"] = operationType;
            AddList(parameters, "UserId", userIDs);
            AddList(parameters, "UserGroup", userGroups);
            AddList(parameters, "ProductCode", productCodes);

            return this.Do("ModifyImageAttribute", parameters);
        }






        public ClientResponse DescribeImageAttribute(string imageID, string attribute)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["ImageId"] = imageID;
            parameters["Attribute"] = attribute;

            return this.Do("DescribeImageAttribute", parameters);
        }



        public ClientResponse DeregisterImage(string imageID)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["ImageId"] = imageID;

            return this.Do("DeregisterImage", parameters);

        }

     

        public ClientResponse DeleteKeyPair(string keyName)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["KeyName"] = keyName;

            return this.Do("DeleteKeyPair", parameters);

        }

        public ClientResponse CreateKeyPair(string keyName)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["KeyName"] = keyName;

            return this.Do("CreateKeyPair", parameters);
        }

        public ClientResponse ConfirmProductInstance(string productCode, string instanceID)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["ProductCode"] = productCode;
            parameters["InstanceId"] = instanceID;

            return this.Do("ConfirmProductInstance", parameters);
        }

        public ClientResponse RevokeSecurityGroupIngress(string groupName, string sourceSecurityGroupName, string sourceSecurityGroupOwnerID)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["GroupName"] = groupName;
            parameters["SourceSecurityGroupName"] = sourceSecurityGroupName;
            parameters["SourceSecurityGroupOwnerId"] = sourceSecurityGroupOwnerID;

            return this.Do("RevokeSecurityGroupIngress", parameters);
        }

        public ClientResponse RevokeSecurityGroupIngress(string groupName, IPProtocol ipProtocol, int fromPort, int toPort, string cidrIp)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["GroupName"] = groupName;
            parameters["IpProtocol"] = ipProtocol.ToString().ToLower();
            parameters["FromPort"] = fromPort.ToString();
            parameters["ToPort"] = toPort.ToString();
            parameters["CidrIp"] = cidrIp;

            return this.Do("RevokeSecurityGroupIngress", parameters);

        }


        public ClientResponse AuthorizeSecurityGroupIngress(string groupName, string sourceSecurityGroupName, string sourceSecurityGroupOwnerID)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["GroupName"] = groupName;
            parameters["SourceSecurityGroupName"] = sourceSecurityGroupName;
            parameters["SourceSecurityGroupOwnerId"] = sourceSecurityGroupOwnerID;

            return this.Do("AuthorizeSecurityGroupIngress", parameters);
        }


        public ClientResponse AuthorizeSecurityGroupIngress(string groupName, IPProtocol ipProtocol, int fromPort, int toPort, string cidrIp)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["GroupName"] = groupName;
            parameters["IpProtocol"] = ipProtocol.ToString().ToLower();
            parameters["FromPort"] = fromPort.ToString();
            parameters["ToPort"] = toPort.ToString();
            parameters["CidrIp"] = cidrIp;

            return this.Do("AuthorizeSecurityGroupIngress", parameters);

        }


        public ClientResponse DeleteSecurityGroup(string groupName)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["GroupName"] = groupName;

            return this.Do("DeleteSecurityGroup", parameters);
        }
        public ClientResponse CreateSecurityGroup(string groupName, string groupDescription)
        {

            NameValuePairs parameters = new NameValuePairs();
            parameters["GroupName"] = groupName;
            parameters["GroupDescription"] = groupDescription;

            return this.Do("CreateSecurityGroup", parameters);
        }






        public ClientResponse GetConsoleOutput(string instanceID)
        {
            NameValuePairs parameters = new NameValuePairs();
            parameters["InstanceId"] = instanceID;
            return this.Do("GetConsoleOutput", parameters);
        }



        public ClientResponse DescribeSecurityGroups(params string[] groupNames)
        {
            NameValuePairs parameters = new NameValuePairs();
            AddList(parameters, "GroupName", groupNames);
            return this.Do("DescribeSecurityGroups", parameters);
        }



        public ClientResponse DescribeKeyPairs(params string[] keyPairIDs)
        {


            NameValuePairs parameters = new NameValuePairs();
            AddList(parameters, "KeyName", keyPairIDs);
            return this.Do("DescribeKeyPairs", parameters);


        }


        public ClientResponse DescribeImages(string[] imageIDs, string[] owners, string[] executableBy)
        {
            NameValuePairs parameters = new NameValuePairs();
            AddList(parameters, "ImageId", imageIDs);
            AddList(parameters, "Owner", owners);
            AddList(parameters, "ExecutableBy", executableBy);
            return this.Do("DescribeImages", parameters);


        }


       

    



















        private void AddOptional(NameValuePairs parameters, string name, string value)
        {
            if (!string.IsNullOrEmpty(value))
            {
                parameters[name] = value;
            }
        }

        private void AddList(NameValuePairs parameters, string name, string[] values)
        {
            for (int i = 0; i < values.Length; i++)
            {
                parameters[name + "." + (i + 1)] = values[i];
            }
        }

        private ClientResponse Do(string action, NameValuePairs parameters)
        {
            parameters["Action"] = action;

            parameters["AWSAccessKeyId"] = _accessKeyID;
            parameters["SignatureVersion"] = "1";
            parameters["Version"] = "2007-01-19";
            parameters["Timestamp"] = Utils.Iso8601Date(DateTime.Now);
            parameters["Signature"] = this.GetSignature(parameters);

            return this.GetRepresentation(GetRequest.Create("", parameters));

        }

        private string GetSignature(NameValuePairs parameters)
        {
            string canonicalString = Fn.Join("", Fn.Select<string, string>(Fn.OrderBy(parameters.Keys), delegate(string item)
            {
                return item + parameters[item];
            }));

            return Utils.ToBase64(Utils.ToHMACSHA1(Encoding.UTF8, _secretAccessKey, canonicalString));
        }













        public void DescribeInstances( string[] instanceIDs , Action<ClientResponse> callback)
        {
            NameValuePairs parameters = new NameValuePairs();
            AddList(parameters, "InstanceId", instanceIDs);
            this.Do("DescribeInstances", parameters, callback);
        }



         public ClientResponse DescribeInstances(params string[] instanceIDs)
        {
            return Utils.MakeSynchronous<string[], ClientResponse>( instanceIDs, DescribeInstances);
            //NameValuePairs parameters = new NameValuePairs();
            //AddList(parameters, "InstanceId", instanceIDs);
            //return this.Do("DescribeInstances", parameters);

        }

         private void Do(string action, NameValuePairs parameters, Action<ClientResponse> callback)
         {
             parameters["Action"] = action;

             parameters["AWSAccessKeyId"] = _accessKeyID;
             parameters["SignatureVersion"] = "1";
             parameters["Version"] = "2007-01-19";
             parameters["Timestamp"] = Utils.Iso8601Date(DateTime.Now);
             parameters["Signature"] = this.GetSignature(parameters);

             this.GetRepresentation(GetRequest.Create("", parameters), callback);

         }





    }


       

}
